/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2015-2018. All rights reserved.
 * Description: Signature Verify CBB Library
 * Author: tangzonglei
 * Create: 2015
 * History: 2018/11/23 yebin code rule fixes
 */
#include "../pki/cmscbb_pki.h"
#include "cmscbb_sdk.h"
#include "../asn1/cmscbb_asn1_utils.h"
#include "../cmscbb_common/cmscbb_common.h"
#include "../cmscbb_common/cmscbb_list.h"
#include "../x509/cmscbb_x509.h"

#if CMSCBB_WITHOUT_SECUREC == 0 && CMSCBB_SUPPORT_CRL_COMPARE == 1
#include "securec.h"
#endif

#define CVB_MAX_VERIFY_DEPTH 10

typedef struct CmscbbPkiStoreSt {
    LIST_OF(CmscbbX509Cert) cert_store;
    LIST_OF(CmscbbX509Crl) crl_store;
} CmscbbPkiStore;

typedef struct CmscbbPkiSt {
    CmscbbPkiStore pki_store;
} CmscbbPki;

CMSCBB_ERROR_CODE CmscbbPkiInit(CmscbbVrf* vrf)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbPki* pki = CVB_NULL;

    if (vrf == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    ret = CmscbbMallocWith0((CVB_VOID**)&pki, sizeof(CmscbbPki));
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    vrf->pkiCtx = (CVB_VOID*)pki;

    return CVB_SUCCESS;
}

CVB_VOID CmscbbPkiUninit(CmscbbVrf* vrf)
{
    CmscbbPki* pki = CVB_NULL;
    if (vrf == CVB_NULL) {
        return;
    }

    pki = (CmscbbPki*)vrf->pkiCtx;
    if (pki == CVB_NULL) {
        return;
    }

    /* free trust store */
    CMSCBB_LIST_FREE(&(pki->pki_store.cert_store), CmscbbX509FreeCert);
    CMSCBB_LIST_FREE(&(pki->pki_store.crl_store), CmscbbX509FreeCrl);

    CmscbbFree((CVB_VOID*)(vrf->pkiCtx));
    vrf->pkiCtx = CVB_NULL;
}

CMSCBB_ERROR_CODE CmscbbPkiStoreAddCert(const CmscbbVrf* vrf, CmscbbX509Cert* cert)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbPki* pki = CVB_NULL;
    LIST_OF(CmscbbX509Cert)* certList = CVB_NULL;
    CVB_INT iter;

    if (vrf == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    pki = (CmscbbPki*)vrf->pkiCtx;
    if (cert == CVB_NULL || pki == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    /* check duplicate certificate in store */
    certList = &(pki->pki_store.cert_store);
    for (iter = 0; iter < (CVB_INT)certList->num; ++iter) {
        const CmscbbX509Cert* tempCert = certList->data[iter];

        /* check duplicate */
        if (CMSCBB_COMPARE_ASN_BITS(&tempCert->signature, &cert->signature) == 0) {
            if (CmscbbCompareAsnOcts(&tempCert->rawSigned, &cert->rawSigned) != 0) {
                return CMSCBB_ERR_PKI_CERT_INVALID_CONTENT;
            }
            return CMSCBB_ERR_PKI_CERT_ALREADY_EXIST;
        }
    }

    ret = CMSCBB_LIST_ADD(certList, cert);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    /* add certificate reference */
    ++cert->iRef;
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbPkiStoreAddCrl(const CmscbbVrf* vrf, CmscbbX509Crl* crl)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbPki* pki = CVB_NULL;
    LIST_OF(CmscbbX509Crl)* crlList = CVB_NULL;
    CVB_INT iter;

    if (vrf == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    pki = (CmscbbPki*)vrf->pkiCtx;
    if (crl == CVB_NULL || pki == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    /* check duplicate CRL */
    crlList = &(pki->pki_store.crl_store);
    for (iter = 0; iter < (CVB_INT)crlList->num; ++iter) {
        const CmscbbX509Crl* tempCrl = crlList->data[iter];

        /* check duplicate */
        if (CMSCBB_COMPARE_ASN_BITS(&tempCrl->signature, &crl->signature) == 0) {
            return CMSCBB_ERR_PKI_CRL_ALREADY_EXIST;
        }
    }

    ret = CMSCBB_LIST_ADD(crlList, crl);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    /* Add CRL reference */
    ++crl->iRef;
    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalCheckCertIssuer
 * Description  : check subject certificate's issuer by signature
 * Params
 *   [IN] subjCert:  subject X509 certificate
 *   [IN] authorCert: author X509 certificate
 * Return Value : CMSCBB_ERROR_CODE
 * Remarks      : only check the signature, no attributes check,
 *   attributes should be check by situation.
 *
 *   Date              Author     Modification
 *   2015/10/14 12:19  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckCertIssuer(CmscbbX509Cert* subjCert, const CmscbbX509Cert* authorCert)
{
    CMSCBB_ERROR_CODE ret;
    CVB_BOOL verifyResult = CVB_FALSE;

    if (subjCert->issuer != CVB_NULL) {
        if (subjCert->issuer == authorCert) {
            /* already checked */
            return CVB_SUCCESS;
        } else {
            return CMSCBB_ERR_PKI_CERT_INVALID_ISSUER;
        }
    }

    /* compare issuer and subject */
    ret = CmscbbCompareX509Name(&(subjCert->toBeSigned.issuer), &(authorCert->toBeSigned.subject));
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    if (((CVB_UINT32)authorCert->toBeSigned.extensions.ku & CMSCBB_X509_KU_KEY_CERT_SIGN) == 0) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CERT_UNMATCHED_PURPOSE, CVB_NULL);
        return (CMSCBB_ERR_PKI_CERT_UNMATCHED_PURPOSE);
    }
#if CMSCBB_ALLOW_NO_CHECK_TSA_CRL
    if (((CVB_UINT32)authorCert->toBeSigned.extensions.ku & CMSCBB_X509_KU_CRL_SIGN) == 0) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CERT_UNMATCHED_PURPOSE, CVB_NULL);
        return (CMSCBB_ERR_PKI_CERT_UNMATCHED_PURPOSE);
    }
#endif

    ret = CmscbbX509PubKeyVerify(&(subjCert->rawSigned), &(subjCert->signature),
        &(authorCert->toBeSigned.subjectPubKey), &(subjCert->algorithm), &verifyResult);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    if (verifyResult != CVB_TRUE) {
        return CMSCBB_ERR_PKI_CERT_INVALID_ISSUER;
    }

    subjCert->issuer = authorCert;
    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalFindCertIssuer
 * Description  : find certificate issuer from store
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] subjectCert: subject X509 certificate
 * Return Value : Issuer of the certificate, or CVB_NULL if not found
 *   Date              Author     Modification
 *   2015/11/11 12:22  tangzonglei  Create
 */
CVB_STATIC CmscbbX509Cert* InternalFindCertIssuer(const CmscbbVrf* vrf, CmscbbX509Cert* subjectCert)
{
    CmscbbPki* pki = (CmscbbPki*)vrf->pkiCtx;
    CVB_INT iter;
    CmscbbX509Cert* issuerCert = CVB_NULL;

    for (iter = 0; iter < (CVB_INT)(pki->pki_store.cert_store.num); ++iter) {
        CmscbbX509Cert* cert = pki->pki_store.cert_store.data[iter];

        if ((CVB_BOOL)cert->toBeSigned.extensions.caInfo.isCa != CVB_TRUE) {
            continue;
        }

        if (InternalCheckCertIssuer(subjectCert, cert) == CVB_SUCCESS) {
            issuerCert = cert;
            break;
        }
    }

    return issuerCert;
}

/*
 * Prototype    : InternalCheckCrlIssuer
 * Description  : check key usage and verify the CRL.
 * Params
 *   [IN] crl:X509 crl
 *   [IN] authorCert: author X509 certificate
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/11 12:23  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckCrlIssuer(const CmscbbX509Crl* crl, const CmscbbX509Cert* authorCert)
{
    CMSCBB_ERROR_CODE ret;
    CVB_BOOL isSignatureValid = CVB_FALSE;

    ret = CmscbbCompareX509Name(&(crl->tbsCertList.issuer), &(authorCert->toBeSigned.subject));
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    if (((CVB_UINT32)authorCert->toBeSigned.extensions.ku & CMSCBB_X509_KU_CRL_SIGN) == 0) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CERT_UNMATCHED_PURPOSE, CVB_NULL);
        return (CMSCBB_ERR_PKI_CERT_UNMATCHED_PURPOSE);
    }

    ret = CmscbbX509PubKeyVerify(&(crl->rawSigned), &(crl->signature),
        &(authorCert->toBeSigned.subjectPubKey), &(crl->algorithm), &isSignatureValid);
    if (CVB_FAILED(ret) || isSignatureValid != CVB_TRUE) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CRL_INVALID_ISSUER, CVB_NULL);
        return (CMSCBB_ERR_PKI_CRL_INVALID_ISSUER);
    }

    return CVB_SUCCESS;
}

CVB_STATIC CmscbbX509Cert* InternalPkiFindCrlIssuer(const CmscbbVrf* vrf, const CmscbbX509Crl* crl)
{
    CmscbbPki* pki = CVB_NULL;
    CVB_INT iter;
    CmscbbX509Cert* authorCert = CVB_NULL;

    if (vrf == CVB_NULL) {
        return CVB_NULL;
    }

    pki = (CmscbbPki*)vrf->pkiCtx;
    if (pki == CVB_NULL || crl == CVB_NULL) {
        return CVB_NULL;
    }

    for (iter = 0; iter < (CVB_INT)(pki->pki_store.cert_store.num); ++iter) {
        CmscbbX509Cert* cert = pki->pki_store.cert_store.data[iter];
        if (InternalCheckCrlIssuer(crl, cert) == CVB_SUCCESS) {
            authorCert = cert;
            break;
        }
    }

    return authorCert;
}

/*
 * Prototype    : InternalFindCertRoot
 * Description  : Find root of the certificate.
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] cert: X509 certificate
 *   [IN] findDepth: current find depth
 * Return Value : Root certificate, or CVB_NULL if not found.
 *   Date              Author     Modification
 *   2015/11/11 12:23  tangzonglei  Create
 */
CVB_STATIC CmscbbX509Cert* InternalFindCertRoot(CmscbbVrf* vrf, CmscbbX509Cert* cert, CVB_INT findDepth)
{
    CmscbbX509Cert* issuer = CVB_NULL;
    CmscbbX509Cert* root = CVB_NULL;

    CVB_BOOL isSelfSigned = CVB_FALSE;
    CVB_INT nextDepth = findDepth;

    /* return if find depth is too high */
    if (nextDepth < 0 || nextDepth > CVB_MAX_VERIFY_DEPTH) {
        CVB_LOG_ERROR(CMSCBB_ERR_UNDEFINED, "find path too deep.");
        return CVB_NULL;
    }

    ++nextDepth;

    /* if cert is root, return itself */
    if (CmscbbX509IsSelfSigned(cert, &isSelfSigned) != CVB_SUCCESS) {
        return CVB_NULL;
    }

    if (isSelfSigned) {
        return cert;
    }

    /* find issuer of the certificate */
    issuer = InternalFindCertIssuer(vrf, cert);
    if (issuer == CVB_NULL) {
        return CVB_NULL;
    }

    /* recursively find the issuer's root */
    root = InternalFindCertRoot(vrf, issuer, nextDepth);
    return root;
}

/* certificate is within CRL scope, serials number included */
#define CVB_CRL_SCORE_SCOPE		0x080
/* CRL times valid */
#define CVB_CRL_SCORE_TIME      0x040
/* crl has same issuer with certificate */
#define CVB_CRL_SCORE_ISSUER    0x020
#if CMSCBB_SUPPORT_INDIRECT_CRL
/* indirect crl */
#define CVB_CRL_SCORE_INDIRECT  0x010
#endif

/* CmscbbCrlScoreInfo */
typedef struct CmscbbCrlScoreInfoSt {
    CVB_UINT32 crlScore;    /* CRL score */
    CmscbbX509Crl* crl;  /* the CRL */
} CmscbbCrlScoreInfo;
DECLARE_LIST_OF(CmscbbCrlScoreInfo);

/*
 * Prototype    : InternalCrlScoreSortDesc
 * Description  : callback implement for list sort
 * Params
 *   [IN] csi1: crl score information 1
 *   [IN] csi2: crl score information 2
 * Return Value : compare result.
 *   Date              Author     Modification
 *   2015/11/11 12:23  tangzonglei  Create
 */
CVB_STATIC CVB_INT InternalCrlScoreSortDesc(const CmscbbCrlScoreInfo* csi1, const CmscbbCrlScoreInfo* csi2)
{
    /* sort in descending order */
    if (csi1->crlScore > csi2->crlScore) {
        return -1;
    } else if (csi1->crlScore == csi2->crlScore) {
        return 0;
    } else {
        return 1;
    }
}

CVB_STATIC CVB_BOOL InternalIsIndirectCrl(const CmscbbX509Crl* crl)
{
#if CMSCBB_SUPPORT_INDIRECT_CRL
    /* check if the CRL is indirect for the certificate,
         * both the CRL's indirect flag is on and its issuer name different with certificate's */
    return (CVB_BOOL)crl->tbsCertList.extensions.idp.indirectCRL;
#else
    (CVB_VOID)crl;
    return CVB_FALSE;
#endif
}
/*
 * Prototype    : InternalCrlGetIssuerScore
 * Description  : check issuer of crl, and give a relative score
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] crl: X509 crl
 *   [IN] cert: X509 certificate
 *   [IN] crtRoot:X095 Root crl
 *   [OUT] score: crl score
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/11 12:23  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCrlGetIssuerScore(CmscbbVrf* vrf, const CmscbbX509Crl* crl,
    const CmscbbX509Cert* cert, const CmscbbX509Cert* crtRoot, CVB_UINT32* score)
{
    CmscbbX509Cert* crlRoot = CVB_NULL;
    CmscbbX509Cert* authorCert = CVB_NULL;
    CVB_BOOL equalIssuerName = CVB_FALSE;
    CVB_BOOL indirectCrl;
    const CmscbbX509Name* certIssuer;

    certIssuer = &(cert->toBeSigned.issuer);
    if (certIssuer->names.num == 0) {
        return CMSCBB_ERR_UNDEFINED;
    }

    if (CmscbbCompareX509Name(certIssuer, &(crl->tbsCertList.issuer)) == 0) {
        equalIssuerName = CVB_TRUE;
    }

    indirectCrl = InternalIsIndirectCrl(crl);
    if (equalIssuerName != CVB_TRUE && indirectCrl != CVB_TRUE) {
        return CMSCBB_ERR_UNDEFINED;
    }

    /* verify signature */
    authorCert = InternalPkiFindCrlIssuer(vrf, crl);
    if (authorCert == CVB_NULL) {
        return CMSCBB_ERR_UNDEFINED;
    }

    /* must issued by the same root */
    crlRoot = InternalFindCertRoot(vrf, authorCert, 0);
    if (crtRoot != crlRoot) {
        return CMSCBB_ERR_UNDEFINED;
    }

    if (equalIssuerName == CVB_TRUE) {
        *score |= CVB_CRL_SCORE_ISSUER;
    }

#if CMSCBB_SUPPORT_INDIRECT_CRL
    if (indirectCrl == CVB_TRUE) {
        *score |= CVB_CRL_SCORE_INDIRECT;
    }
#endif

    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalCrlGetScore
 * Description  : get total score for the CRL
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] crl: X509 crl
 *   [IN] cert: X509 certificate
 *   [IN] crtRoot: X509 crl root
 * Return Value : the score of the CRL
 * Remarks      : If the CRL's revoke list contains cert, it could be the best crl for the cert even CRL expired
 *   Date              Author     Modification
 *   2015/11/11 12:23  tangzonglei  Create
 */
CVB_STATIC CVB_INT InternalCrlGetScore(CmscbbVrf* vrf, const CmscbbX509Crl* crl, const CmscbbX509Cert* cert,
    const CmscbbX509Cert* crtRoot)
{
    CVB_UINT32 crlScore = 0;
    CVB_TIME_T tmNextUpdate = 0;

    /* get score relative to issuer */
    if (CVB_FAILED(InternalCrlGetIssuerScore(vrf, crl, cert, crtRoot, &crlScore))) {
        return 0;
    }

    if (crlScore == 0) {
        CVB_LOG_WARNING(0, "invalid issuer score.");
        return 0;
    }

    /* get score relative to revoke info */
    if (crl->tbsCertList.revokedCerts.revokedList.num > 0) {
        CVB_INT iter;
        const SET_OF(CmscbbX509RevokeEntry)* revokedList;

        /* find cert revoke info */
        revokedList = &(crl->tbsCertList.revokedCerts.revokedList);
        for (iter = 0; iter < (CVB_INT)revokedList->num; ++iter) {
            const CmscbbX509RevokeEntry* revoked = &(revokedList->data[iter]);
            if (CmscbbCompareAsnOcts((const CmscbbAsnOcts*) &revoked->userCert,
                (const CmscbbAsnOcts*) &(cert->toBeSigned.serialNumber)) == 0) {
                /* found certificate */
                crlScore |= CVB_CRL_SCORE_SCOPE;
                break;
            }
        }
    }

    /* check time */
    if (CmscbbConvertDatetimeToTime(&(crl->tbsCertList.nextUpdateTime), &tmNextUpdate) != CVB_SUCCESS) {
        return 0;
    }

    if (tmNextUpdate >= vrf->baseTime) {
        crlScore |= CVB_CRL_SCORE_TIME;
    } else {
        CVB_LOG_WARNING(CMSCBB_ERR_PKI_CRL_HAS_EXPIRED, "Found an expired CRL, it most likely cause NO CRL error.");
        if ((crlScore & CVB_CRL_SCORE_SCOPE) == 0) {
            /* reset the score for expired CRL whose revoke list not contain the cert */
            crlScore = 0;
        }
    }

    return (CVB_INT)crlScore;
}

/*
 * Prototype    : InternalGetScoredCrl
 * Description  : filter CRLs in store according to the score.
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] cert:X509 certificate
 *   [IN] crlSiList:list of crl score information
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/11 12:23  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalGetScoredCrl(CmscbbVrf* vrf, CmscbbX509Cert* cert,
    LIST_OF(CmscbbCrlScoreInfo)* crlSiList)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbPki* pki = (CmscbbPki*)(vrf->pkiCtx);
    const CmscbbX509Cert* crtRoot;
    CVB_INT iter;

    /* find the best CRL for cert */
    crtRoot = InternalFindCertRoot(vrf, cert, 0);
    if (crtRoot == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CERT_ISSUER_NOT_FOUND, CVB_NULL);
        return (CMSCBB_ERR_PKI_CERT_ISSUER_NOT_FOUND);
    }

    for (iter = 0; iter < (CVB_INT)pki->pki_store.crl_store.num; ++iter) {
        CmscbbX509Crl* crl;
        CVB_UINT32 crlScore;

        crl = pki->pki_store.crl_store.data[iter];
        if (crl == CVB_NULL) {
            continue;
        }

        /* get CRL score */
        crlScore = (CVB_UINT32)InternalCrlGetScore(vrf, crl, cert, crtRoot);

        if (crlScore > 0) {
            CmscbbCrlScoreInfo* crlScoreInfo = CVB_NULL;
            ret = CmscbbMallocWith0((CVB_VOID**)&crlScoreInfo, sizeof(CmscbbCrlScoreInfo));
            if (CVB_FAILED(ret)) {
                CVB_LOG_DEBUG(ret, CVB_NULL);
                return ret;
            }

            crlScoreInfo->crlScore = crlScore;
            crlScoreInfo->crl = crl;
            ret = CMSCBB_LIST_ADD(crlSiList, crlScoreInfo);
            if (CVB_FAILED(ret)) {
                CmscbbFreeWithNullCheck(crlScoreInfo);
                return ret;
            }
        }
    }

    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalFindBestCrlOfCert
 * Description  : find best CRL for the certificate
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] cert: X509 certificate
 * Return Value : The best CRL, or CVB_NULL if not found.
 *   Date              Author     Modification
 *   2015/11/11 12:23  tangzonglei  Create
 */
CVB_STATIC CmscbbX509Crl* InternalFindBestCrlOfCert(CmscbbVrf* vrf, CmscbbX509Cert* cert, CVB_BOOL isTsRelative)
{
    CMSCBB_ERROR_CODE ret;
    LIST_OF(CmscbbCrlScoreInfo) crlSiList = {0};
    CmscbbX509Crl* bestCrl = CVB_NULL;
    CVB_INT iter;

    ret = InternalGetScoredCrl(vrf, cert, &crlSiList);
    CVB_GOTO_ERR_IF_FAIL(ret);

    /* sort the CRL by score in descending order */
    ret = CMSCBB_LIST_SORT(&crlSiList, InternalCrlScoreSortDesc);
    CVB_GOTO_ERR_IF_FAIL(ret);

    /* the valid CRL with highest score is the best */
    for (iter = 0; iter < (CVB_INT)crlSiList.num; ++iter) {
        CVB_BOOL result = CVB_FALSE;
        CmscbbX509Cert* authorCert = CVB_NULL;
        CmscbbCrlScoreInfo* crlSi = crlSiList.data[iter];

        if (crlSi == CVB_NULL || crlSi->crl == CVB_NULL) {
            continue;
        }

        authorCert = InternalPkiFindCrlIssuer(vrf, crlSi->crl);
        if (authorCert == CVB_NULL) {
            CVB_LOG_INFO(0, "There's no issuer found for the scored CRL");
            continue;
        }

        ret = CmscbbPkiVerifyCert(vrf, authorCert, CVB_FALSE, isTsRelative, &result);
        if (!CVB_FAILED(ret) && result == CVB_TRUE) {
            bestCrl = crlSi->crl;
            break;
        } else {
            CVB_LOG_INFO(ret, "Failed to verify issuer of scored CRL.");
        }
    }

    if (crlSiList.num == 0) {
        CVB_LOG_ERROR(ret, "Can't find CRL for certificate.");
        ret = CMSCBB_ERR_PKI_CERT_NO_CRL;
    } else if (bestCrl == CVB_NULL) {
        CVB_LOG_ERROR(ret, "Verify CRL failed.");
        ret = (ret != CVB_SUCCESS) ? ret : CMSCBB_ERR_PKI_CRL_INVALID_ISSUER;
    }
CVB_ERR:
    vrf->lastErr = ret;
    CMSCBB_LIST_FREE(&crlSiList, CmscbbFreeWithNullCheck);
    return bestCrl;
}

/*
 * Prototype    : InternalIsCertRevoked
 * Description  : Check if the certificate is revoked
 * Params
 *   [IN] cert:  X509 certificate
 *   [IN] baseTime: base time
 *   [IN] isTsRelative: if usage of certificate relatived with timestamp
 *   [IN] revoked: X509 revoked Item
 * Return Value : return CVB_SUCCESS if not revoked.
 *   Date              Author     Modification
 *   2015/11/11 12:23  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalIsCertRevoked(const CmscbbX509Cert* cert, CVB_TIME_T baseTime,
    CVB_BOOL isTsRelative, const CmscbbX509RevokeEntry* revoked)
{
    CMSCBB_ERROR_CODE ret;
    CVB_TIME_T revokeTime = 0;

    /* revoke sn match */
    if (CmscbbCompareAsnOcts(&revoked->userCert, &(cert->toBeSigned.serialNumber)) == 0) {
        /* for ts relative, check reason */
        if (isTsRelative == CVB_TRUE || cert->toBeSigned.extensions.caInfo.isCa) {
            CmscbbX509RevokeReason reason = (CmscbbX509RevokeReason)revoked->attrs.reason;

            if (reason != RVRS_AFFILIATIONCHANGED && reason != RVRS_SUPERSEDED && reason != RVRS_CESSATIONOFOPERATION) {
                return CMSCBB_ERR_PKI_CERT_REVOKED;
            }
        }

        /* check revoke time */
        ret = CmscbbConvertDatetimeToTime(&(revoked->revocationDate), &revokeTime);
        if (CVB_FAILED(ret)) {
            CVB_LOG_DEBUG(ret, CVB_NULL);
            return ret;
        }

        if (baseTime >= revokeTime) {
            return CMSCBB_ERR_PKI_CERT_REVOKED; /* it's revoked certificate */
        }
    }
    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalCheckCrlOfCert
 * Description  : check revoke status of certificate
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] cert: X509 certificate
 *   [OUT] crl: output the best CRL for the certificate
 * Return Value : CMSCBB_ERR_PKI_CERT_REVOKED if the certificate revoked
 *   CVB_SUCCESS if not revoked, or other indicate error.
 *   Date              Author     Modification
 *   2015/11/11 12:24  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckCrlOfCert(CmscbbVrf* vrf, CmscbbX509Cert* cert, CVB_BOOL isTsRelative,
    CmscbbX509Crl** crl)
{
    CMSCBB_ERROR_CODE ret = 0;
    CmscbbX509Crl* tempCrl = CVB_NULL;
    CVB_INT iter;
    SET_OF(CmscbbX509RevokeEntry)* revokedList = CVB_NULL;

    tempCrl = InternalFindBestCrlOfCert(vrf, cert, isTsRelative);
    if (tempCrl == CVB_NULL) {
        CVB_LOG_DEBUG(vrf->lastErr, CVB_NULL);
        return (vrf->lastErr);
    }

    /* check revocation list */
    if (tempCrl->tbsCertList.revokedCerts.revokedList.num > 0) {
        revokedList = &(tempCrl->tbsCertList.revokedCerts.revokedList);
        for (iter = 0; iter < (CVB_INT)revokedList->num; ++iter) {
            CmscbbX509RevokeEntry* revoked = &(revokedList->data[iter]);

            ret = InternalIsCertRevoked(cert, vrf->baseTime, isTsRelative, revoked);
            if (ret == CMSCBB_ERR_PKI_CERT_REVOKED) {
                CVB_LOG_ERROR(ret, "The certificate was revoked.\r\n");
                break;
            }

            if (CVB_FAILED(ret)) {
                CVB_LOG_DEBUG(ret, CVB_NULL);
                return ret;
            }
        }
    }

    *crl = tempCrl;
    return ret;
}

/*
 * Prototype    : InternalCheckCertDate
 * Description  : check if the certificate is valid in date time.
 * Params
 *   [IN] subjCert: X509 subject certificate
 *   [IN] baseTime: base time
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/11 12:24  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckCertDate(const CmscbbX509Cert* subjCert, CVB_TIME_T baseTime)
{
    CMSCBB_ERROR_CODE ret;
    CVB_TIME_T tmValidateFrom = 0;
    CVB_TIME_T tmValidateTo = 0;

    /* check cert time */
    ret = CmscbbConvertDatetimeToTime(&(subjCert->toBeSigned.validity.notBefore), &tmValidateFrom);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    ret = CmscbbConvertDatetimeToTime(&(subjCert->toBeSigned.validity.notAfter), &tmValidateTo);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    if (baseTime < tmValidateFrom) {
        return CMSCBB_ERR_PKI_CERT_DATETIME_NO_VALID_YET;
    }

    if (baseTime > tmValidateTo) {
        return CMSCBB_ERR_PKI_CERT_DATETIME_EXPIRED;
    }

    return CVB_SUCCESS;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckCrl(CmscbbVrf* vrf, CmscbbX509Cert* subjCert, CVB_BOOL isTsRelative,
    CVB_BOOL *verifyDone)
{
    CMSCBB_ERROR_CODE ret;
#if CMSCBB_ENABLE_VERIFY_WITHOUT_CRL
    CVB_BOOL checkCrl = CVB_FALSE;
#else
    CVB_BOOL checkCrl = CVB_TRUE;
#endif

#if CMSCBB_ALLOW_NO_CHECK_TSA_CRL
    if (checkCrl) {
        if (subjCert->toBeSigned.extensions.exku == CMSCBB_PKI_XKU_TIMESTAMP) {
            checkCrl = CVB_FALSE;
        }
    }
#endif

    if (checkCrl) { /* Check_crl will change the value when the CMSCBB_ALLOW_NO_CHECK_TSA_CRL macro is turned on */
        CmscbbX509Crl* crl = CVB_NULL;
        /* check CRL */
        ret = InternalCheckCrlOfCert(vrf, subjCert, isTsRelative, &crl);
        if (CVB_FAILED(ret)) {
            CVB_LOG_DEBUG(ret, CVB_NULL);
            return ret;
        }

#if CMSCBB_SUPPORT_INDIRECT_CRL
        if ((CVB_BOOL)crl->tbsCertList.extensions.idp.indirectCRL == CVB_FALSE) {
            *verifyDone = CVB_TRUE;
            return CVB_SUCCESS;
        }
#else
    *verifyDone = CVB_TRUE;
    return CVB_SUCCESS;
#endif
    }
    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalVerifyCertificate
 * Description  : verify certificate itself
 * Params
 *   [IN] vrf: data for verify context
 *   [IN] subjCert: X509 subject certificate
 *   [IN] author: athor X509 certificate
 *   [IN] verifyDone: if verify success return CVB_SUCCESS
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/12/14 15:16  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalVerifyCertificate(CmscbbVrf* vrf, CmscbbX509Cert* subjCert, CVB_BOOL isTsRelative,
    CmscbbX509Cert** author, CVB_BOOL *verifyDone)
{
    CMSCBB_ERROR_CODE ret;
    CVB_BOOL isSelfSigned = CVB_FALSE;
    CmscbbX509Cert* authorCert = CVB_NULL;

    ret = InternalCheckCertDate(subjCert, vrf->baseTime);
    if (CVB_FAILED(ret)) {
        CVB_LOG_WARNING(ret, "Check date for certificate failed, verify may fail.");
        return ret;
    }

    ret = CmscbbX509IsSelfSigned(subjCert, &isSelfSigned);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    if (isSelfSigned) {
        *verifyDone = CVB_TRUE;
        return CVB_SUCCESS;
    }

    ret = InternalCheckCrl(vrf, subjCert, isTsRelative, verifyDone);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    /* check issuer */
    authorCert = InternalFindCertIssuer(vrf, subjCert);
    if (authorCert == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CERT_ISSUER_NOT_FOUND, CVB_NULL);
        return (CMSCBB_ERR_PKI_CERT_ISSUER_NOT_FOUND);
    }
    ret = CmscbbCheckAlgoPara(&(authorCert->toBeSigned.subjectPubKey.algorithm), &(subjCert->algorithm));
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    *author = authorCert;
    return CVB_SUCCESS;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamPkiVerifyCert(const CmscbbVrf* vrf, const CmscbbX509Cert* cert,
    const CVB_BOOL* result)
{
    CVB_BOOL legalFlag = (vrf == CVB_NULL) || (cert == CVB_NULL) || (result == CVB_NULL);
    if (legalFlag) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbPkiVerifyCert(CmscbbVrf* vrf, CmscbbX509Cert* cert, CVB_BOOL resetDepth,
    CVB_BOOL isTsRelative, CVB_BOOL* result)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbX509Cert* subjCert = cert;

    ret = InternalCheckParamPkiVerifyCert(vrf, cert, result);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    *result = CVB_FALSE;

    if (resetDepth) {
        vrf->currDepth = 0;
    }

    if (vrf->currDepth > CVB_MAX_VERIFY_DEPTH) {
        CVB_LOG_ERROR(0, "The path of certificate is too deep.");
        return CMSCBB_ERR_PKI_CERT_DEPTH;
    }

    /* increase depth */
    ++vrf->currDepth;
    CVB_LOG_DEBUG1(0, "Current depth of certificate path is: %d.", vrf->currDepth);

    /* check code sign extension */
    while (subjCert != CVB_NULL) {
        CmscbbX509Cert* authorCert = CVB_NULL;
        CVB_BOOL verifyDone = CVB_FALSE;
        ret = InternalVerifyCertificate(vrf, subjCert, isTsRelative, &authorCert, &verifyDone);
        if (CVB_FAILED(ret)) {
            CVB_LOG_INFO(ret, "Failed to verify certificate itself");
            break;
        }

        if (verifyDone) {
            break;
        }

        /* continue validate issuer */
        subjCert = authorCert;
    }

    if (CVB_FAILED(ret)) {
        *result = CVB_FALSE;
    } else {
        *result = CVB_TRUE;
    }

    --vrf->currDepth; /* decrease depth */
    return ret;
}

CVB_STATIC CVB_BOOL InternalCheckParamPkiFindCertByIssuerSN(const CmscbbVrf* vrf, const CmscbbX509Name* issuer,
    const CmscbbAsnBigint* sn)
{
    CVB_BOOL legalFlag = (vrf == CVB_NULL) || (issuer == CVB_NULL) || (sn == CVB_NULL) ||
                         (issuer->names.num == 0) || (sn->len == 0);
    if (legalFlag) {
        return CVB_FALSE;
    }
    return CVB_TRUE;
}

CmscbbX509Cert* CmscbbPkiFindCertByIssuerSn(const CmscbbVrf* vrf, const CmscbbX509Name* issuer,
    const CmscbbAsnBigint* sn)
{
    CmscbbX509Cert* certExpected = CVB_NULL;
    CVB_INT iter;
    const LIST_OF(CmscbbX509Cert)* certs = CVB_NULL;
    CmscbbPki* pki = CVB_NULL;

    if (!InternalCheckParamPkiFindCertByIssuerSN(vrf, issuer, sn)) {
        return CVB_NULL;
    }

    pki = (CmscbbPki*)vrf->pkiCtx;
    if (pki == CVB_NULL) {
        return CVB_NULL;
    }

    certs = &(pki->pki_store.cert_store);
    for (iter = 0; iter < (CVB_INT)(certs->num); ++iter) {
        CmscbbX509Cert* cert = certs->data[iter];
        if (cert == CVB_NULL) {
            continue;
        }

        if (CmscbbCompareX509Name(issuer, &(cert->toBeSigned.issuer)) == 0 &&
            CmscbbCompareAsnOcts(sn, &(cert->toBeSigned.serialNumber)) == 0) {
            certExpected = cert;
            break;
        }
    }
    return certExpected;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParamPkiVerifyCertList(CmscbbVrf* vrf)
{
    if (vrf == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbPkiVerifyCertList(CmscbbVrf* vrf)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbPki* pki = CVB_NULL;
    LIST_OF(CmscbbX509Cert)* certList = CVB_NULL;
    CVB_INT iter;
    CVB_INT iter1;
    CVB_BOOL verifyResult = CVB_FALSE;

    ret = InternalCheckParamPkiVerifyCertList(vrf);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    pki = (CmscbbPki*)vrf->pkiCtx;
    if (pki == NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    certList = &(pki->pki_store.cert_store);
    for (iter = 0; iter < (CVB_INT)certList->num; ++iter) {
        CmscbbX509Cert* subCert = certList->data[iter];
        if (subCert->issuer != NULL) {
            continue;
        }

        for (iter1 = 0; iter1 < (CVB_INT)certList->num; ++iter1) {
            const CmscbbX509Cert* iussuerCert = certList->data[iter1];

            if (CmscbbCompareX509Name(&(subCert->toBeSigned.issuer), &(iussuerCert->toBeSigned.subject)) != 0) {
                continue;
            }
            ret = CmscbbX509PubKeyVerify(&(subCert->rawSigned), &(subCert->signature),
                &(iussuerCert->toBeSigned.subjectPubKey),
                &(subCert->algorithm), &verifyResult);
            if (CVB_FAILED(ret) || verifyResult == CVB_FALSE) {
                CVB_LOG_ERROR(CMSCBB_ERR_PKI_CERT_VERIFY_FAILED, CVB_NULL);
                return CMSCBB_ERR_PKI_CERT_VERIFY_FAILED;
            }
            subCert->issuer = iussuerCert;
            break;
        }
    }
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbGetCertAndCrlList(CmscbbVrf* ctx, LIST_OF(CmscbbX509Cert)** certList,
    LIST_OF(CmscbbX509Crl)** crlList)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbPki* pki = CVB_NULL;
    ret = InternalCheckParamPkiVerifyCertList(ctx);
    if (CVB_FAILED(ret)) {
        return ret;
    }
    if (certList == NULL && crlList == NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    pki = (CmscbbPki*)ctx->pkiCtx;
    if (certList != NULL) {
        *certList = &(pki->pki_store.cert_store);
    }
    if (crlList != NULL) {
        *crlList = &(pki->pki_store.crl_store);
    }
    return CVB_SUCCESS;
}

#if CMSCBB_SUPPORT_CRL_COMPARE
/*
 * Prototype    : InternalGetRevokeList
 * Description  : Copy the revocation information in the CRL to the CRL information structure
 * Params
 *   [IN] revokeList: revoke list
 *   [IN] x509Revoke: x509 certificate need to revoke
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2016/05/19 16:04  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalGetRevokeList(CmscbbRevokeList* revokeList, const CmscbbX509Revoked* x509Revoke)
{
    CMSCBB_ERROR_CODE ret;
    CVB_INT iter;
    CVB_INT revoked = (CVB_INT)x509Revoke->revokedList.num;

    if (revoked <= 0) {
        return CVB_SUCCESS;
    }

    /* create revoke list memory */
    ret = CmscbbMallocWith0((CVB_VOID**)&revokeList->snList, (CVB_UINT32)revoked * sizeof(CmscbbSerialNum));
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    for (iter = 0; iter < revoked; ++iter) {
        /* get next empty revoke info entry */
        CmscbbSerialNum* revokeInfo = revokeList->snList + revokeList->revokeCount;
        const CmscbbX509RevokeEntry* revokeEntry = &(x509Revoke->revokedList.data[iter]);

        if (revokeEntry->userCert.len == 0) {
            continue;
        }

        if (revokeEntry->userCert.len > CMSCBB_MAX_SN_LEN) {
            continue;
        }

        ret = (CMSCBB_ERROR_CODE)memcpy_s(revokeInfo->sn, CMSCBB_MAX_SN_LEN, revokeEntry->userCert.octs,
            revokeEntry->userCert.len);
        if (CVB_FAILED(ret)) {
            revokeInfo->snLenth = 0;
        } else {
            revokeInfo->snLenth = revokeEntry->userCert.len;
        }
        ++revokeList->revokeCount;
    }

    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbGetCrlIsAuthored(CmscbbVrf* vrf, CmscbbCrlInfo* crlInfo, const CmscbbX509Crl* crl)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbX509Cert* authorCert;
    CVB_BOOL res = CVB_FALSE;

    authorCert = InternalPkiFindCrlIssuer(vrf, crl);
    if (authorCert != CVB_NULL) {
        CVB_BOOL isSelfSigned = 0;

        vrf->baseTime = (CVB_TIME_T)crlInfo->updateTime;
        ret = CmscbbPkiVerifyCert(vrf, authorCert, CVB_TRUE, CVB_TRUE, &res);
        if (ret == CVB_SUCCESS && res == CVB_TRUE) {
            crlInfo->valid = CVB_TRUE;
        }

        ret = CmscbbX509IsSelfSigned(authorCert, &isSelfSigned);
        if (CVB_FAILED(ret)) {
            return ret;
        }
        if (isSelfSigned && crl->tbsCertList.revokedCerts.revokedList.num != 0) {
            ret = InternalGetRevokeList(&crlInfo->revokeList, &(crl->tbsCertList.revokedCerts));
            if (CVB_FAILED(ret)) {
                return ret;
            }
        }
        if (authorCert->toBeSigned.serialNumber.len <= CMSCBB_MAX_SN_LEN) {
            ret = (CMSCBB_ERROR_CODE)memcpy_s(crlInfo->issuerSn.sn, CMSCBB_MAX_SN_LEN,
                authorCert->toBeSigned.serialNumber.octs, authorCert->toBeSigned.serialNumber.len);
            if (CVB_FAILED(ret)) {
                CmscbbFreeWithNullCheck(crlInfo->revokeList.snList);
                crlInfo->revokeList.snList = CVB_NULL;
                return ret;
            }
            crlInfo->issuerSn.snLenth = authorCert->toBeSigned.serialNumber.len;
        }
    }

    return CVB_SUCCESS;
}
#endif /* CMSCBB_SUPPORT_CRL_COMPARE */
